package com.studay;
//拓展添加数据的类型 使用泛型



//设计动态数组接口
//这个E 类型的意思 E其实就是泛型 任何类型都可以
public class ArrayList<E> {
    /*
    元素的数量
     */
    private int size;
    //所有的元素
    private E[] elements;//数组

    public static final int DEFAULT_CAPACITY= 10;
    //一般的话 常数都通过这样定义来使用

    public static final int ELEMENT_NOT_FOUND= -1;

    public ArrayList(int capacity){
        //如果参数是负数的话或者比10小，那么我们默认把数组大小默认为10，如果大于则为传进来的参数
        capacity = (capacity < DEFAULT_CAPACITY)? DEFAULT_CAPACITY:capacity;
        elements = (E[]) new Object[capacity];
    }

    public ArrayList(){
        this(DEFAULT_CAPACITY);//无函数调有参
    }


    public int size(){
        return size;
    }; // 元素的数量

    public boolean isEmpty(){
        return size == 0;
    }; // 是否为空


    public boolean contains(E element){//其实就是遍历 这个元素和数组中的元素是否相同不同就false
//        for (int i = 0; i =< size; i++) {
//            if(element == elements[i]){
//                return true;
//            }
//        }
//        return false;
        return indexOf(element) != ELEMENT_NOT_FOUND;
    } // 是否包含某个元素


    public void add(E element){
//        elements[size] =element;
//        size++;
//        ----------------
        add(size,element);//直接用后面那个add

    }// 添加元素到最后面


    public E get(int index){// 返回index位置对应的元素
//        if (index >size || index< 0){
////这做法是不  return 0;//如果指定的索引不符合要求 那么通常做法是 抛出一个异常
//            throw new IndexOutOfBoundsException("Index: "+ index+ "，Size： " +size);
//        }

        rangeCheck(index);
        return elements[index];
    }


    public E set(int index, E  element){
        //如果放入的索引不符合则要抛出异常
//        if ( index < 0 || index > size){
//            throw new IndexOutOfBoundsException("Index: "+index+"，Size"+ size);
//        }
        rangeCheck(index);

        //其实相当于用新的元素覆盖旧的元素
        E old = elements[index];//取出原来的元素
        elements[index] = element;//新的覆盖
        return old;


    } // 设置index位置的元素


    public void add(int index, E element){
//        if ( index < 0 || index > size){
//            throw new IndexOutOfBoundsException("Index: "+index+"，Size"+ size);
//        }
        rangeCheckForAdd(index);
        ensureCapacity(size+1);//添加的时候容易超出数组范围 此时需要扩容

        // 往index位置添加元素 那么要从最后后面的元素往后挪
//        --------for (int i = size-1; i >= index; i--) 优化~
        for (int i = size; i > index; i--) {
            elements[i] = elements[i-1];
        }
        elements[index] = element;
        size++;
    }


    public E remove(int index){// 删除index位置对应的元素
//        if ( index < 0 || index > size){
//            throw new IndexOutOfBoundsException("Index: "+index+"，Size"+ size);
//        }
        rangeCheck(index);

        E old = elements[index];
    //因为数组是连续的 删除一个元素 后面的元素要向前挪
        for (int i = index+1; i < size; i++) {//从要删除的那个元素的后一个开始动
            elements[i-1] = elements[i];

//            size--;//!!挪完元素后再-- 最后一个元素不处理 与size =0 相似
        }
        size--;
        //（变更，加一句） 对象数组 需要释放最后一个数组的对象数据elements[size] =null;
        elements[size] =null;
        return old;
    }


    public void remove(E element){//传一个元素 把索引干掉
        remove(indexOf(element));//传一个元素 把索引干掉

    }


    public int indexOf(E element){// 查看元素的位置
    //查看元素的位置 就是看输入元素的索引？ 遍历 找到这个元素 返回索引
//        --------------------------
        //NULL值的处理
        if (element == null){
            for (int i = 0; i < size; i++) {
                if (elements[i] ==null) return i;
                //因为这个 elements[i] 可能为null 所以不能用equals！
                //如果传进来的是null那么 直接返回他的位置即可
            }
        }else {
            for (int i = 0; i < size; i++) {
                if (element.equals(elements[i])){
                    //如果不重写equals 他默认比较的是内存地址值
                    //Integer内有重写equals的方法 他们两个比较的是数字是否相等
                    return i;
                }
            }

        }

        return ELEMENT_NOT_FOUND;
    }


    public void clear(){// 清除所有元素
//    size = 0;//不需要销毁内存 浪费时间和性能
//        ---------------------------------
        //当为 对象数组 的时候 size = 0
        // 虽然访问不到 但是对象依旧存在 依然会占用空间
        //不能用 elements  = null;//这会将数组地址和对象一起摧毁
        //    下次还要new浪费性能

        for (int i = 0; i < size ; i++) {
            elements[i] = null;
            //这样能把里面的地址指向的 对象数据 清除！
        }
        size = 0;//地址还在 但是不给访问
    }


    public String toString(){
        //格式 size = 3, [99,11,22]
        StringBuilder bf = new StringBuilder();
        bf.append("size = ").append(size).append(",[");
        for (int i = 0; i < size; i++) {
            if (i != 0){
                bf.append(" ");
            }
          bf.append(elements[i]);
//          if ( i < size -1){
//              bf.append(" ");
//          }
        }
        bf.append("]");
        return bf.toString();
    }
    //因为前面 判断索引越界和抛出异常太频繁，通常需要封装一下
    private void outOfBounds(int index){
        throw new IndexOutOfBoundsException("Index: "+index+"，Size"+ size);
    }

    private void rangeCheck(int index){
        if ( index < 0 || index >= size){
            outOfBounds(index);
        }
    }

    private void rangeCheckForAdd(int index){
        if ( index < 0 || index > size){
            outOfBounds(index);
        }
    }


    //保证Capacity的
    private void ensureCapacity(int capacity){
            int oldCapacity = elements.length;
            if (oldCapacity >= capacity) return;
            //不够用了 来扩容！
          //这里扩容为1.5倍
            int newCapacity = oldCapacity + ( oldCapacity >> 1);
            //创建一个新的扩容后的数组 将旧的数据移过来
            E[] newElements = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;//新数组引用覆盖旧的引用
        //这样是线程不安全的

        System.out.println(oldCapacity+"扩容为"+newCapacity);
            //位运算左边移一位 乘以2   右移一位 除以2
    }


}
